<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>ArduPilot MAGFit</title>
<link rel="icon" href="../images/AP_favicon.png">
<script src='../modules/plotly.js/dist/plotly.min.js'></script>

<script src='../modules/build/matrix/matrix.umd.js'></script>

<script src="../modules/build/floating-ui/dist/umd/popper.min.js"></script>
<script src="../modules/build/tippyjs/dist/tippy-bundle.umd.min.js"></script>

<script type="text/javascript" src="wmm.js"></script>
<script type="text/javascript" src="quaternion.js"></script>
<script type="text/javascript" src="../Libraries/Array_Math.js"></script>
<script type="text/javascript" src="../Libraries/Plotly_helpers.js"></script>
<script type="text/javascript" src="../Libraries/Param_Helpers.js"></script>
<script type="text/javascript" src="../Libraries/ParameterMetadata.js"></script>
<script type="text/javascript" src="../Libraries/OpenIn.js"></script>
<script type="text/javascript" src="../Libraries/LoadingOverlay.js"></script>
<script type="text/javascript" src="../Libraries/DecodeDevID.js"></script>
<script type="text/javascript" src="../Libraries/FileSaver.js"></script>
<script type="text/javascript" src="../Libraries/LogHelpers.js"></script>
<script type="text/javascript" src="magfit.js"></script>

</head>

<style>
    div.plotly-notifier {
        visibility: hidden;
    }

    .tippy-box {
        font-size: 20px;
    }
</style>

<table style="width:1200px"><tr><td>
    <a href="https://ardupilot.org"><img src="../images/ArduPilot.png"></a>
</td><td>
    <a href="https://github.com/ArduPilot/WebTools"><img src="../images/github-mark.png" style="width:60px"></a>
    <br>
    <a href="https://github.com/ArduPilot/WebTools"><img src="../images/GitHub_Logo.png" style="width:60px"></a>
</td></tr></table>

<table><tr><td style="width:1200px">
<h1 style="text-align:center"><a href="" style="color: #000000; text-decoration:none;">ArduPilot MAGFit in flight compass calibration</a></h1>
</td></tr></table>

<body>

<p style="width:1150px; text-align:justify; padding-left:30px; margin-block-start: 0; margin-block-end: 0.5em;">
This tool takes a .bin log and performs a "MAGFit inflight calibration". Calibration parameters are found that fit the measured magnetic field from the log to the expected field from the World Magnetic Model.
For best results use a flight log that covers as many orientations as possible. Flying figure of eight patterns works well. If you need a motor calibration using either throttle or a current reading then you should also cover as wide a range of throttles and current draws as possible. You do not have to be in any particular flight mode.
</p>

<table>
<tr>
<td style="width: 30px;"></td>
<td>
<fieldset style="width:1100px">
    <legend>Setup</legend>
    <table>
        <td>
            <fieldset style="width:200px;height:80px">
                <legend>Analysis time
                    <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
                    data-tippy-content='Start and end times for MAGFit, automatically updated from the selected portion of the log shown in the Flight Data plot. The Calculate button must be clicked after any change of start/end time.'
                    data-tippy-maxWidth='750px'/>
                </legend>
                <label for="TimeStart">Start (s)</label>
                <input id="TimeStart" name="TimeStart" type="number" min="0" step="1" value="0" onchange="time_range_changed()" style="width:75px"/><br><br>
                <label for="TimeEnd">End (s)</label>
                <input id="TimeEnd" name="TimeEnd" type="number" min="0" step="1" value="0" onchange="time_range_changed()" style="width:75px"/>
            </fieldset>
        </td>
        <td style="width: 10px;"></td>
        <td>
            <fieldset style="width:200px;height:80px" id="ATTITUDE">
                <legend>Attitude source
                    <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
                    data-tippy-content='Select the attitude source that will be used for the MAGFit, this should best represent the true attitude of the vehicle, typically the default is fine, if you know there were EKF issues using DCM may give better results. The Calculate button must be clicked after any change of source.'
                    data-tippy-maxWidth='750px'/>
                </legend>
            </fieldset>
        </td>
        <td style="width: 10px;"></td>
        <td>
            <input id="fileItem" type="file" accept=".bin" onchange="readFile(this)"><br><br><br>
            <input type="button" id="calculate" value="Calculate" onclick="loading_call(() => { calculate(); })">
        </td>
        <td style="width: 300px;"></td>
        <td style="text-align:right; vertical-align:top">
            <input id="OpenIn" type="button" value="Open In" disabled><br><br><br>
            <img id="TT" src="../images/question-circle.svg" width="30px" 
                data-tippy-content='Load a .bin log file using the file button. "Open In" opens the log in one of the other WebTools. If Analysis time or Attitude source are changed the Calculate button should be used to re-calculate the MAGFit.',
                data-tippy-maxWidth='750px'
            />
        </td>
    </table>
</fieldset>
</td>
</tr>
</table>

<table><tr><td style="width:1200px">
    <h2 style="text-align:center">Flight Data
        <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
        data-tippy-content='Zoom into a section of the flight to change the Analysis time then click "Calculate". Selecting the flying section of the log with heading changes and various throttle levels should give the best results.',
        data-tippy-maxWidth='750px'/>
    </h2>
</td></tr></table>

<div id="FlightData" style="width:1200px;height:450px"></div>

<table><tr><td style="width:1200px">
    <h2 style="text-align:center">Expected vs measured body frame magnetic field
        <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
        data-tippy-content='Per compass status and options, use checkboxes to select calibrations for comparison. Plots show results of calibrations in X Y and Z axis.'
        data-tippy-maxWidth='750px'/>
    </h2>
</td></tr></table>

<table>
    <tr>
        <td style="vertical-align:top">
            <fieldset style="width:350px">
                <legend>Compass 1</legend>
                <p id="MAG0"></p>
            </fieldset>
        </td>
        <td style="vertical-align:top">
            <fieldset style="width:350px">
                <legend>Compass 2</legend>
                <p id="MAG1"></p>
            </fieldset>
        </td>
        <td style="vertical-align:top">
            <fieldset style="width:350px">
                <legend>Compass 3</legend>
                <p id="MAG2"></p>
            </fieldset>
        </td>
    </tr>
</table>

<div id="mag_plot_x" style="width:1200px;height:300px"></div>
<div id="mag_plot_y" style="width:1200px;height:300px"></div>
<div id="mag_plot_z" style="width:1200px;height:300px"></div>

<table><tr><td style="width:1200px">
    <h2 style="text-align:center">Yaw change
        <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
        data-tippy-content='Change in yaw resulting from MAGFit. The first plot is change in yaw between new MAGFit calibration and the existing calibration. 
                            The second plot is projected change in yaw between new MAGFit calibration and the selected attitude source.'
        data-tippy-maxWidth='750px'/>
    </h2>
</td></tr></table>

<div id="yaw_change_mag" style="width:1200px;height:300px"></div>
<div id="yaw_change_att" style="width:1200px;height:300px"></div>

<table><tr><td style="width:1200px">
    <h2 style="text-align:center">Field length
        <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
        data-tippy-content='Comparison of field length &radic;(X&#178; + Y&#178; + Z&#178;). field length errors may coincide with current draw.
                            Significant correlation could mean using battery compensation would give better results, if correlation remains when using a compensated calibration a hardware fix may been required to move the compass further from the interference source.'
        data-tippy-maxWidth='750px'/>
    </h2>
</td></tr></table>

<div id="field_length" style="width:1200px;height:300px"></div>

<table><tr><td style="width:1200px">
    <h2 style="text-align:center">Motor compensation sources
        <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
        data-tippy-content='Battery current for comparison with field length and calibration error. 
                            Significant correlation could mean using battery compensation would give better results, if correlation remains when using a compensated calibration a hardware fix may been required to move the compass further from the interference source.'
        data-tippy-maxWidth='750px'/>
    </h2>
</td></tr></table>

<div id="motor_comp" style="width:1200px;height:300px"></div>

<table><tr><td style="width:1200px">
    <h2 style="text-align:center">Calibration error
        <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
        data-tippy-content='First plot shows the error (e.g. difference) over time between the selected calibrations and the expected world magnetic model. Second plot is mean error compared for all selected, the value is the normalized area under the curve of the first plot. 
        Error over time can be used to correlate with compensation sources. Mean error can be used to compare between calibrations.'
        data-tippy-maxWidth='750px'/>
    </h2>
</td></tr></table>

<div id="error_plot" style="width:1200px;height:300px"></div>
<div id="error_bars" style="width:1200px;height:300px"></div>

<table>
    <tr>
        <td>
            <fieldset>
                <legend>Compass 1 parameters
                    <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
                    data-tippy-content='Selected calibration parameters for the first compass. These will be saved in the downloaded parameter file if "Save Parameters" is clicked.'
                    data-tippy-maxWidth='750px'/>
                </legend>
                <p id="MAG_1_PARAM_INFO"></p>
                <p>
                    <input id="COMPASS_ORIENT" name="COMPASS_ORIENT" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_OFS_X" name="COMPASS_OFS_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_OFS_Y" name="COMPASS_OFS_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_OFS_Z" name="COMPASS_OFS_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_DIA_X" name="COMPASS_DIA_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_DIA_Y" name="COMPASS_DIA_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_DIA_Z" name="COMPASS_DIA_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_ODI_X" name="COMPASS_ODI_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_ODI_Y" name="COMPASS_ODI_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_ODI_Z" name="COMPASS_ODI_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_MOT_X" name="COMPASS_MOT_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_MOT_Y" name="COMPASS_MOT_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_MOT_Z" name="COMPASS_MOT_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_SCALE" name="COMPASS_SCALE" type="number" style="width: 100px"/>
                </p>
            </fieldset>
        </td>
        <td>
            <fieldset style="width:350px">
                <legend>Compass 2 parameters
                    <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
                    data-tippy-content='Selected calibration parameters for the second compass. These will be saved in the downloaded parameter file if "Save Parameters" is clicked.'
                    data-tippy-maxWidth='750px'/>
                </legend>
                <p id="MAG_2_PARAM_INFO"></p>
                <p>
                    <input id="COMPASS_ORIENT2" name="COMPASS_ORIENT2" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_OFS2_X" name="COMPASS_OFS2_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_OFS2_Y" name="COMPASS_OFS2_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_OFS2_Z" name="COMPASS_OFS2_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_DIA2_X" name="COMPASS_DIA2_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_DIA2_Y" name="COMPASS_DIA2_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_DIA2_Z" name="COMPASS_DIA2_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_ODI2_X" name="COMPASS_ODI2_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_ODI2_Y" name="COMPASS_ODI2_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_ODI2_Z" name="COMPASS_ODI2_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_MOT2_X" name="COMPASS_MOT2_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_MOT2_Y" name="COMPASS_MOT2_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_MOT2_Z" name="COMPASS_MOT2_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_SCALE2" name="COMPASS_SCALE2" type="number" style="width: 100px"/>
                </p>
            </fieldset>
        </td>
        <td>
            <fieldset style="width:350px">
                <legend>Compass 3 parameters
                    <img id="TT" src="../images/question-circle.svg" style="width: 1em; vertical-align: bottom" 
                    data-tippy-content='Selected calibration parameters for the third compass. These will be saved in the downloaded parameter file if "Save Parameters" is clicked.'
                    data-tippy-maxWidth='750px'/>
                </legend>
                <p id="MAG_3_PARAM_INFO"></p>
                <p>
                    <input id="COMPASS_ORIENT3" name="COMPASS_ORIENT3" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_OFS3_X" name="COMPASS_OFS3_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_OFS3_Y" name="COMPASS_OFS3_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_OFS3_Z" name="COMPASS_OFS3_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_DIA3_X" name="COMPASS_DIA3_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_DIA3_Y" name="COMPASS_DIA3_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_DIA3_Z" name="COMPASS_DIA3_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_ODI3_X" name="COMPASS_ODI3_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_ODI3_Y" name="COMPASS_ODI3_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_ODI3_Z" name="COMPASS_ODI3_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_MOT3_X" name="COMPASS_MOT3_X" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_MOT3_Y" name="COMPASS_MOT3_Y" type="number" style="width: 100px"/>
                </p><p>
                    <input id="COMPASS_MOT3_Z" name="COMPASS_MOT3_Z" type="number" style="width: 100px"/>
                </p>
                <p>
                    <input id="COMPASS_SCALE3" name="COMPASS_SCALE3" type="number" style="width: 100px"/>
                </p>
            </fieldset>
        </td>
    </tr>
</table>
<table style="width:1150px">
    <tr>
        <td style="text-align:right">
            <br>
            <input type="button" id="SaveParams" value="Save Parameters" onclick="save_parameters();">
        </td>
    </tr>
</table>


</body>

<script>

    window.onerror = function(msg, url, linenumber) {
        alert('Sorry, something went wrong.\n\n' + 
                'Please try a hard reload of this page to clear its cache.\n\n' +
                'If the error persists open an issue on the GitHub repo.\n' +
                'Include a copy of the log and the following error message:\n\n' +
                msg + '\n' +
                'URL: '+ url +'\n' +
                'Line Number: '+ linenumber)
        return false
    }
    window.addEventListener('unhandledrejection', function (e) {
        throw new Error(e.reason.stack)
    })

    // Load tool tips
    tippy('#TT')

    // load parameter metadata
    let params = []
    for (let i = 0; i < 3; i++) {
        const names = get_compass_param_names(i+1)
        params.push(...names.offsets)
        params.push(...names.diagonals)
        params.push(...names.off_diagonals)
        params.push(names.scale)
        params.push(...names.motor)
        params.push(names.orientation)
    }
    load_param_inputs("params.json", params)

    // Disable manual param input
    for (const param of params) {
        parameter_set_disable(param, true)
    }

    setup_plots() 

    function readFile(e) {
        const file = e.files[0]
        if (file == null) {
            return
        }
        let reader = new FileReader()
        reader.onload = function (e) {
            loading_call( async () => { 
                await load(reader.result)
                document.title = "MAGFit: " + file.name
            })
        }
        reader.readAsArrayBuffer(file)
    }

    const open_in_update = setup_open_in("OpenIn", "fileItem", function (data) { loading_call(() => { load(data) }) })

    init_loading_overlay()

</script>
